package com.wyh.socket_demo.endpoint;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.wyh.socket_demo.entiy.FilePacket;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.ByteChannel;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.util.EnumSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * @author wyh
 * @version 1.0
 * @date 2022/4/12 14:39
 */
@Component
@ServerEndpoint("/inserver/{username}/{fileName}")
public class SocketServerDown {
    private static final Logger log = LoggerFactory.getLogger(SocketServerDown.class);

    /**
     * 记录当前在线连接数
     */
    public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("username") String username, @PathParam("fileName") String fileName) {
        fileName += ".pdf";
        sessionMap.put(username, session);
        log.info("有新用户加入，username={}, 当前在线人数为：{}", username, sessionMap.size());
        System.out.println("request send file : " + fileName);
        FilePacket p = FilePacket.constructNewFilePacket(fileName);
        try {
            session.getBasicRemote().sendObject(p.getBuffer().array());
        } catch (IOException | EncodeException e) {
            e.printStackTrace();
        }

    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session, @PathParam("username") String username) {
        sessionMap.remove(username);
        log.info("有一连接关闭，移除username={}的用户session, 当前在线人数为：{}", username, sessionMap.size());
    }

    /**
     * 收到客户端消息后调用的方法
     * 后台收到客户端发送过来的消息
     * onMessage 是一个消息的中转站
     * 接受 浏览器端 socket.send 发送过来的 json数据
     *
     *
     */
    @OnMessage
    public void onMessage(ByteBuffer bytes, Session session, @PathParam("username") String username)  {
        FilePacket p = FilePacket.parseByteBuffer(bytes);
        int code;
        switch (p.getType()) {
            case FilePacket.P_ACK_NEW_FILE:
                code = (int)p.getBuffer().get();
                if(FilePacket.SUCCESS_CODE == code){
                    System.out.println("server accept file request");
                    startSendFileData(session,"G://");
                }
                break;
            case FilePacket.P_ACK_FILE_END:
                code = (int)p.getBuffer().get();
                if(FilePacket.SUCCESS_CODE == code){
                    System.out.println("server save file sucessfully");
                    try {
                        session.close();
                    }catch (Exception e){
                        System.out.println(e);
                    }

                }
                break;
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    private void startSendFileData(Session session,String path) {
        Runnable runnable = new Runnable() {
            @SneakyThrows
            public void run() {

                try {
                    ByteChannel fileChannel = Files.newByteChannel(Paths.get(path), EnumSet.of(StandardOpenOption.READ));
                    ByteBuffer buffer = ByteBuffer.allocate(1 + 4096);
                    buffer.order(ByteOrder.BIG_ENDIAN);

                    MessageDigest md = MessageDigest.getInstance("MD5");

                    int bytesRead = -1;

                    buffer.clear();//make buffer ready for write
                    buffer.put((byte)FilePacket.P_FILE_DATA);

                    while((bytesRead = fileChannel.read(buffer)) != -1){
                        buffer.flip();  //make buffer ready for read
                        buffer.mark();
                        buffer.get(); //skip a byte
                        md.update(buffer);
                        buffer.reset();
                        session.getBasicRemote().sendObject(buffer);
                        buffer.clear(); //make buffer ready for write
                        buffer.put((byte)FilePacket.P_FILE_DATA);
                    }

                    byte[] digest = md.digest();
                    String digestInHex = DatatypeConverter.printHexBinary(digest).toUpperCase();
                    System.out.println("send file finished, digest: " + digestInHex);
                    FilePacket p = FilePacket.constructFileEndPacket(digestInHex);
                    session.getBasicRemote().sendObject(p.getBuffer());
                } catch (Exception e) {
                    session.close();
                }
            }
        };

        new Thread(runnable).start();

    }

}
